package epoch.game.core.util;

import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.apache.commons.beanutils.BeanUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import epoch.game.core.serialize.ISerializable;
import epoch.game.core.serialize.StreamReader;
import epoch.game.core.serialize.StreamWriter;

/**
 * 
 * @author will
 * @date 2015年6月12日 下午5:02:29
 */
public class BeanUtil {

	/**
	 * Logger for this class
	 */
	private static final Logger logger = LogManager.getLogger(BeanUtil.class);

	public static String getStack() {
		Exception exception = new Exception();
		StackTraceElement[] stackTrace = exception.getStackTrace();
		StringBuffer buffer = new StringBuffer();
		buffer.append("\n");
		for (StackTraceElement stackTraceElement : stackTrace) {
			buffer.append(stackTraceElement.getClassName()).append(".").append(stackTraceElement.getMethodName()).append(".").append(stackTraceElement.getLineNumber()).append("\n");
		}
		return buffer.toString();
	}
	
	public static String getStack(StackTraceElement[] stackTrace) {
		StringBuffer buffer = new StringBuffer();
		buffer.append("\n");
		for (StackTraceElement stackTraceElement : stackTrace) {
			buffer.append(stackTraceElement.getClassName()).append(".").append(stackTraceElement.getMethodName()).append(".").append(stackTraceElement.getLineNumber()).append("\n");
		}
		return buffer.toString();
	}
	
	public static String getStack(Throwable throwable) {
		StackTraceElement[] stackTrace = throwable.getStackTrace();
		StringBuffer buffer = new StringBuffer();
		buffer.append("\n");
		for (StackTraceElement stackTraceElement : stackTrace) {
			buffer.append(stackTraceElement.getClassName()).append(".").append(stackTraceElement.getMethodName()).append(".").append(stackTraceElement.getLineNumber()).append("\n");
		}
		return buffer.toString();
	}

	/**
	 * 通过反射获取对象的值
	 *
	 * @param obj
	 *            对象
	 * @param properties
	 *            字段名
	 * @return 字段所对应的值
	 * @throws Exception
	 */
	public static Object getMethodValue(Object obj, String properties) throws Exception {
		String methodname = "get" + formatProperties(properties);
		Method method = obj.getClass().getMethod(methodname);
		Object keypropvalue = method.invoke(obj);
		return keypropvalue;
	}

	public static Object invokeMethod(Object obj, Method method) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException {
		return method.invoke(obj);
	}

	/**
	 * 获取BEAN中的属性值 包括私有属性 包括静态属性
	 *
	 * @param obj
	 * @param fieldName
	 * @return
	 * @throws SecurityException
	 * @throws NoSuchFieldException
	 * @throws IllegalArgumentException
	 * @throws IllegalAccessException
	 * @throws InstantiationException
	 */
	public static Object getFieldValue(Object Bean, String fieldName) throws SecurityException, NoSuchFieldException, IllegalArgumentException, IllegalAccessException, InstantiationException {
		Class<? extends Object> cls = Bean.getClass();
		Field declaredField = cls.getDeclaredField(fieldName);
		boolean before = declaredField.isAccessible();
		declaredField.setAccessible(true);
		Object object = declaredField.get(Bean);
		declaredField.setAccessible(before);
		return object;
	}

	public static byte[] convertToByteArray(Object obj) {

		try {
			ObjectOutputStream os = null;
			ByteArrayOutputStream byteStream = new ByteArrayOutputStream(5000);
			os = new ObjectOutputStream(new BufferedOutputStream(byteStream));
			os.flush();
			os.writeObject(obj);
			os.flush();
			byte[] sendBuf = byteStream.toByteArray();
			os.close();
			return sendBuf;
		} catch (IOException e) {
			logger.error(e, e);
		}
		return null;
	}

	public static <K, V> Map<K, V> listToMap(List<V> list, String properties, Map<K, V> map) {
		for (Iterator<V> iter = list.iterator(); iter.hasNext();) {
			V item = iter.next();
			try {
				K key = getPropValue(item, properties);
				map.put(key, item);
			} catch (Exception ex) {
				throw new RuntimeException(ex.getMessage());
			}
		}
		return map;
	}

	public static <K, V> Map<K, V> listToMap(List<V> list, String keyPropertiesName) {
		HashMap<K, V> hash = new HashMap<K, V>();
		return listToMap(list, keyPropertiesName, hash);
	}

	public static void main(String[] args) throws InstantiationException, IllegalAccessException {
		Class<?>[] classes = BeanUtil.class.getClasses();
		for (Class<?> class1 : classes) {
			System.out.println(class1);
		}
		// getClasses(null);
		// System.out.println(formatProperties("fId"));
		// System.out.println(formatProperties("FId"));
		// System.out.println(formatProperties("fid"));

	}

	/**
	 * 首字母大写
	 * 
	 * @param name
	 * @return
	 */
	private static String formatProperties(String name) {
		if (name.length() > 1) {
			if (Character.isLowerCase(name.charAt(0))) {// 小写
				if (Character.isLowerCase(name.charAt(1))) {// 小写
					return String.valueOf(name.charAt(0)).toUpperCase() + name.substring(1);
				} else {
					return String.valueOf(name.charAt(0)).toLowerCase() + name.substring(1);
				}
			} else {
				return name;
			}
		}
		return String.valueOf(name.charAt(0)).toUpperCase();
	}

	@SuppressWarnings("unchecked")
	public static <T> T getPropValue(Object obj, String properties) throws Exception {
		String methodname = "get" + formatProperties(properties);
		Method method = obj.getClass().getMethod(methodname);
		Object keypropvalue = method.invoke(obj);
		return (T) keypropvalue;
	}

	/**
	 *
	 *
	 *
	 * @param oldmap
	 * @param newmap
	 * @param property
	 * @throws Exception
	 */
	public static <K, V> void addOrUpdate(Map<K, V> oldmap, Map<K, V> newmap, String property) throws Exception {
		if (newmap == null) {
			return;
		}
		for (V newobj : newmap.values()) {
			K key = getPropValue(newobj, property);
			V oldobj = oldmap.get(key);
			if (oldobj == null) {
				oldmap.put(key, newobj);
			} else {
				BeanUtils.copyProperties(newobj, oldobj);
			}
		}
	}

	public static <K, V> void addOrUpdate(Map<K, V> oldmap, Collection<V> collection, String property) throws Exception {
		if (collection == null) {
			return;
		}
		for (V newobj : collection) {
			K key = getPropValue(newobj, property);
			V oldobj = oldmap.get(key);
			if (oldobj == null) {
				oldmap.put(key, newobj);
			} else {
				BeanUtils.copyProperties(newobj, oldobj);
			}
		}
	}

	public static boolean isNull(Object object) {
		return object == null;
	}

	public static List<String> toLowerCase(List<String> list) {
		ArrayList<String> result = new ArrayList<String>();
		for (String string : list) {
			result.add(string.toLowerCase());
		}
		return result;
	}

	// long类型转成byte数组
	public static byte[] longToByte(long number) {
		long temp = number;
		byte[] b = new byte[8];
		for (int i = b.length - 1; i >= 0; i--) {
			b[i] = new Long(temp & 0xff).byteValue();// 将最低位保存在最高位
			temp = temp >> 8; // 向右移8位
		}
		return b;
	}

	// byte数组转成long
	public static long byteToLong(byte[] b, int idx) {
		long s = 0;
		idx = idx * 8;
		long s0 = b[idx + 7] & 0xff;// 最低位
		long s1 = b[idx + 6] & 0xff;
		long s2 = b[idx + 5] & 0xff;
		long s3 = b[idx + 4] & 0xff;
		long s4 = b[idx + 3] & 0xff;// 最低位
		long s5 = b[idx + 2] & 0xff;
		long s6 = b[idx + 1] & 0xff;
		long s7 = b[idx + 0] & 0xff;

		// s0不变
		s1 <<= 8;
		s2 <<= 16;
		s3 <<= 24;
		s4 <<= 8 * 4;
		s5 <<= 8 * 5;
		s6 <<= 8 * 6;
		s7 <<= 8 * 7;
		s = s0 | s1 | s2 | s3 | s4 | s5 | s6 | s7;
		return s;
	}

	/**
	 * 注释：int到字节数组的转换！
	 *
	 * @param number
	 * @return
	 */
	public static byte[] intToByte(int number) {
		int temp = number;
		byte[] b = new byte[4];
		for (int i = b.length - 1; i >= 0; i--) {
			b[i] = new Integer(temp & 0xff).byteValue();// 将最低位保存在最高位
			temp = temp >> 8; // 向右移8位
		}
		return b;
	}

	/**
	 * 注释：字节数组到int的转换！
	 *
	 * @param b
	 * @return
	 */
	public static int byteToInt(byte[] b, int idx) {
		int s = 0;
		idx = idx * 4;
		int s0 = b[idx + 3] & 0xff;// 最低位
		int s1 = b[idx + 2] & 0xff;
		int s2 = b[idx + 1] & 0xff;
		int s3 = b[idx + 0] & 0xff;
		s3 <<= 24;
		s2 <<= 16;
		s1 <<= 8;
		s = s0 | s1 | s2 | s3;
		return s;
	}

	/**
	 * 注释：short到字节数组的转换！
	 *
	 * @param s
	 * @return
	 */
	public static byte[] shortToByte(short number) {
		int temp = number;
		byte[] b = new byte[2];
		for (int i = b.length - 1; i >= 0; i--) {
			b[i] = new Integer(temp & 0xff).byteValue();// 将最低位保存在最高位
			temp = temp >> 8; // 向右移8位
		}
		return b;
	}

	/**
	 * 注释：字节数组到short的转换！
	 *
	 * @param b
	 * @return
	 */
	public static short byteToShort(byte[] b, int idx) {
		short s = 0;
		idx = idx * 2;
		short s0 = (short) (b[idx + 1] & 0xff);// 最低位
		short s1 = (short) (b[idx + 0] & 0xff);
		s1 <<= 8;
		s = (short) (s0 | s1);
		return s;
	}
	
	/** 
     * 获取运算数指定位置的值<br> 
     * 例如： 0000 1011 获取其第 0 位的值为 1, 第 2 位 的值为 0<br> 
     *  
     * @param source 
     *            需要运算的数 
     * @param pos 
     *            指定位置 (0<=pos<=7) 
     * @return 指定位置的值(0 or 1) 
     */  
    public static int getBitValue(int source, int pos) {  
        return (int) ((source >> pos) & 1);  
    }  
    
    public static long getBitValue(long source, int pos) {  
        return (long) ((source >> pos) & 1);  
    }  
      
  
    /** 
     * 将运算数指定位置的值置为指定值<br> 
     * 例: 0000 1011 需要更新为 0000 1111, 即第 2 位的值需要置为 1<br> 
     *  
     * @param source 
     *            需要运算的数 
     * @param pos 
     *            指定位置 (0<=pos<=7) 
     * @param value 
     *            只能取值为 0, 或 1, 所有大于0的值作为1处理, 所有小于0的值作为0处理 
     *  
     * @return 运算后的结果数 
     */  
    public static int setBitValue(int source, int pos, int value) {  
  
        int mask = (int) (1 << pos);  
        if (value > 0) {  
            source |= mask;  
        } else {  
            source &= (~mask);  
        }  
  
        return source;  
    }  
      
    /** 
     * 将运算数指定位置的值置为指定值<br> 
     * 例: 0000 1011 需要更新为 0000 1111, 即第 2 位的值需要置为 1<br> 
     *  
     * @param source 
     *            需要运算的数 
     * @param pos 
     *            指定位置 (0<=pos<=7) 
     * @param value 
     *            只能取值为 0, 或 1, 所有大于0的值作为1处理, 所有小于0的值作为0处理 
     *  
     * @return 运算后的结果数 
     */  
    public static long setBitValueLong(long source, int pos, int value) {  
  
        long mask = (long) (1 << pos);  
        if (value > 0) {  
            source |= mask;  
        } else {  
            source &= (~mask);  
        }  
  
        return source;  
    }  
  
    /** 
     * 将运算数指定位置取反值<br> 
     * 例： 0000 1011 指定第 3 位取反, 结果为 0000 0011; 指定第2位取反, 结果为 0000 1111<br> 
     *  
     * @param source 
     *  
     * @param pos 
     *            指定位置 (0<=pos<=7) 
     *  
     * @return 运算后的结果数 
     */  
    public static int reverseBitValue(int source, int pos) {  
        byte mask = (byte) (1 << pos);  
        return (source ^ mask);  
    }  
    
    
    /** 
     * 检查运算数的指定位置是否为1<br> 
     *  
     * @param source 
     *            需要运算的数 
     * @param pos 
     *            指定位置 (0<=pos<=7) 
     * @return true 表示指定位置值为1, false 表示指定位置值为 0 
     */  
    public static boolean checkBitValueLong(long source, int pos) {  
  
        source = (source >>> pos);  
  
        return (source & 1) == 1;  
    } 
  
    /** 
     * 检查运算数的指定位置是否为1<br> 
     *  
     * @param source 
     *            需要运算的数 
     * @param pos 
     *            指定位置 (0<=pos<=7) 
     * @return true 表示指定位置值为1, false 表示指定位置值为 0 
     */  
    public static boolean checkBitValue(int source, int pos) {  
  
        source = (source >>> pos);  
  
        return (source & 1) == 1;  
    } 
    
    /**
	 * 数组转换成十六进制字符串
	 * 
	 * @param byte[]
	 * @return HexString
	 */
	public static final String toHexString(byte[] bArray) {
		StringBuffer sb = new StringBuffer(bArray.length);
		String sTemp;
		for (int i = 0; i < bArray.length; i++) {
			sTemp = Integer.toHexString(0xFF & bArray[i]);
			if (sTemp.length() < 2)
				sb.append(0);
			sb.append(sTemp.toUpperCase());
		}
		return sb.toString();
	}

	/**
	 * 把16进制字符串转换成字节数组
	 * 
	 * @param hexString
	 * @return byte[]
	 */
	public static byte[] parseHexString(String hex) {
		int len = (hex.length() / 2);
		byte[] result = new byte[len];
		char[] achar = hex.toCharArray();
		for (int i = 0; i < len; i++) {
			int pos = i * 2;
			result[i] = (byte) (toByte(achar[pos]) << 4 | toByte(achar[pos + 1]));
		}
		return result;
	}

	private static int toByte(char c) {
		byte b = (byte) "0123456789ABCDEF".indexOf(c);
		return b;
	}

	/**
	 * 深度克隆对象
	 * @param target
	 * @return
	 */
	public static <T> T deepClone(T target){
		StreamWriter writer = new StreamWriter();
		try {
			writer.write(target);
			StreamReader reader = new StreamReader(writer.array());
			T t = reader.read();
			return t;
		} catch (IOException e) {
			logger.error("clone object failed!");
			return null;
		}
	}
    
    public static <T extends ISerializable> byte[] getBytes(T object){
		StreamWriter writer = new StreamWriter();
		writer.writeObject(object);
		return writer.array();
	}
	
	public static <T extends ISerializable> T readBytes(byte[] bytes,Class<T> clasz){
		StreamReader reader = new StreamReader(bytes);
		return reader.readObject(clasz);
	}
}
